package sonic.game;

import sonic.game.character.Sonic;
import sonic.game.tile.Tile;

/**
 * Game
 * 
 * Contains the main game loop
 * 
 * @author Richard Greenlees
 */

public class Game implements Runnable {

	// Let the player be globally accessible since we only ever have one
	public static Sonic player;
	// Make the map globally accessible
	public static GameMap currentMap;
	// And the camera
	public static GameCamera camera;
	public GamePanel gamePanel;
	public Thread gameLoop;
	public boolean bRunning = false;
	/** Deltatime represents the time it took in seconds to complete one game loop. Used to keep things in sync */ 
	public static double deltaTime = 0.015;
	private double startTime;
	private double endTime;
	
	/** Load the test map on creation and start the main loop thread */
	public Game (MainWindow mainWindow)
	{
		loadMap("Maps/Map.txt");
		gamePanel = new GamePanel();
		mainWindow.add(gamePanel);
		gameLoop = new Thread (this, "Main Game Loop");
		gameLoop.start();
	}
	
	public void run()
	{
		bRunning=true;
		// Update the game state and refresh the screen
		while (bRunning)
		{
			startTime = System.nanoTime() / 1000000000.0;
			// Stop low frame rates from affecting the game speed by using deltaTime to update
			update(deltaTime);
			gamePanel.repaint();
			try
			{
				Thread.sleep(10);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
			endTime = System.nanoTime() / 1000000000.0;
			deltaTime = endTime - startTime;
		}
	}
	
	/** Load the given map. Will spawn the player and camera as well */
	public void loadMap(String newMapFile)
	{
		GameMap newMap = new GameMap(newMapFile);
		currentMap = newMap;
		camera = new GameCamera();
		player = new Sonic(currentMap.playerStart);
		// TODO: Allow the map to determine the music rather than hard-coded
		player.playSound("Music/music.wav");
	}
	
	/** Update all relevant tiles. Also handle cleanup of destroyed tiles, and load in pending tiles */
	public void update(double deltaTime)
	{
		camera.updateCam();
		for (Tile a : Game.currentMap.relevantTiles)
		{
			if (a.isRelevant())
			{
				if (a.bDestroyMe)
				{
					currentMap.tilesToDestroy.add(a);
				}
				else
				{
					a.update(deltaTime);
				}
			}
		}
		
		Game.player.update(deltaTime);
		
		// Prevent ConcurrentModificationException
		for (Tile a : Game.currentMap.tilesToDestroy)
		{
			Game.currentMap.relevantTiles.remove(a);
		}
		
		// Prevent ConcurrentModificationException
		for (Tile a : Game.currentMap.pendingTiles)
		{
			Game.currentMap.relevantTiles.add(a);
		}
		
		Game.currentMap.tilesToDestroy.clear();
		Game.currentMap.pendingTiles.clear();
	}
	
}
